package br.biofoco.p2p.rpc.service;

import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.Map.Entry;

import br.biofoco.p2p.peer.PeerNode;
import br.biofoco.p2p.peer.PeerSerializer;
import br.biofoco.p2p.rpc.EndPoint;
import br.biofoco.p2p.rpc.Message;
import br.biofoco.p2p.rpc.Protocol;
import br.biofoco.p2p.rpc.TransportManager;
import br.biofoco.p2p.rpc.messaging.CommandHandler;
import br.biofoco.p2p.rpc.messaging.MessageCode;
import br.biofoco.p2p.rpc.messaging.MessageException;
import br.biofoco.p2p.rpc.messaging.WireRequestMessage;
import br.biofoco.p2p.rpc.messaging.WireResponseMessage;
import br.biofoco.p2p.rpc.messaging.TransportClient;

import com.google.inject.Inject;


public class Messenger implements CommandHandler {
	
	private final Map<String, Command> commandMap = new HashMap<String,Command>();
	private TransportManager transportManager;
	private PeerSerializer peerSerializer;
	
	@Inject
	public Messenger(TransportManager manager) {
		this.transportManager = manager;
		this.peerSerializer = peerSerializer;
		transportManager.setCommandHandler(this);		
	}
	
	public void start() {
		transportManager.startAll();		
	}

	public void stop() {
		transportManager.stopAll();		
	}	
			
	public void register(Command command) {
		commandMap.put(command.getID().toString(), command);
	}
	
	public Command unregisterService(Command service) {
		return commandMap.remove(service.getID());
	}
	
	public Command getService(String name) {
		return commandMap.get(name);
	}
	
	// TODO what to do when evaluate throws an error
	@Override
	public WireResponseMessage doProcess(WireRequestMessage request) {
		
		String command = request.getMethod();
		
		if (command == null || command.trim().isEmpty()) {
			return new WireResponseMessage(request.getId(), MessageCode.ERROR, request.getMethod(), "Invalid service name");
		}
		
		Command service = commandMap.get(command);
		
		if (service == null) {
			return new WireResponseMessage(request.getId(), MessageCode.ERROR, request.getMethod(), "Service not registered");	
		}
		
		try {
			String response = service.execute(request.getParams().toArray(new String[0]));		
			return new WireResponseMessage(request.getId(), MessageCode.OK, request.getMethod(), response);
		} catch (Exception e) {
			return new WireResponseMessage(request.getId(), MessageCode.ERROR, request.getMethod(), e.getMessage());
		}
		
	}
	
	public String sendMessage(String command, PeerNode peerNode, String... params) throws MessageException {
		
		EndPoint endpoint = peerNode.getEndPoint(Protocol.TCP);		
		TransportClient client = null;
		
		try {
			 client = transportManager.getTransportClient(Protocol.TCP, endpoint);
			 String result = client.invokeCommand(command, params);
			 client.close();
			 return result;
		} catch (IOException e) {
			throw new MessageException(e.getMessage(), e);
		} catch (Exception e) {
			throw new MessageException(e.getMessage(), e);
		}
	}

//	public Message sendMessage(Message message) throws MessageException  {
//		
//		PeerNode target = message.getTarget();
//		String command  = message.getCommand();
//		
//		EndPoint endpoint = target.getEndPoint(Protocol.TCP);		
//		TransportClient client = null;
//		
//		String piggyback = peerSerializer.serialize(message.getSender());
//		
//		List<String> params = new ArrayList<String>();
//		for ( Entry<String, String> e : message.getParams().entrySet()) {
//			params.add(e.getKey()+"#"+e.getValue());
//		}
//		
//		try {
//			 client = transportManager.getTransportClient(Protocol.TCP, endpoint);
//			 String result = client.invokeCommand(command, params.toArray(new String[0]));
//			 client.close();
//			 return result;
//		} catch (IOException e) {
//			throw new MessageException(e.getMessage(), e);
//		} catch (Exception e) {
//			throw new MessageException(e.getMessage(), e);
//		}		
//		
//		
//		return null;
//	}

}


